package com.ccx.library.util;
import java.security.SecureRandom;
/**
 * 雪花算法
 */
public class SnowflakeUtil {
    //初始时间戳
    private static final long EPOCH_STAMP = 1262275200000L;
    //序列号id长度
    private static final long SEQUENCE_BIT = 12L;
    //数据id长度
    private static final long MACHINE_BIT = 5L;
    //工作id长度
    private static final long DATA_CENTER_BIT = 5L;
    //序列号最大值
    private static final long MAX_SEQUENCE_NUM = -1L ^ (-1L << SEQUENCE_BIT);
    //工作id需要左移的位数
    private static final long MACHINE_LEFT = SEQUENCE_BIT;
    //数据id需要左移位数
    private static final long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    //时间戳需要左移位数
    private static final long TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT + DATA_CENTER_BIT;
    //上次时间戳，初始值为负数
    private static long lastTimestamp = -1L;
    //机器id默认值
    private static final long MACHINE_ID = 1L;
    //数据id默认值
    private static final long DATACENTER_ID = 1L;
    //序列号默认值
    private static long sequence = 0L;

    //异步获取下一个值
    private static synchronized long getNextValue(Long machineId, long dataCenterId) throws Exception {
        String os = System.getProperty("os.name");
        SecureRandom secureRandom;
        if (os.toLowerCase().startsWith("win")) {
            // windows机器用
            secureRandom = SecureRandom.getInstanceStrong();
        } else {
            // linux机器用
            secureRandom = SecureRandom.getInstance("NativePRNGNonBlocking");
        }
        long currentTimeMillis = currentTimeMillis();
        //获取当前时间戳,如果当前时间戳小于上次时间戳，则时间戳获取出现异常
        if (currentTimeMillis < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", (lastTimestamp - currentTimeMillis)));
        }
        //如果等于上次时间戳（同一毫秒内），则在序列号加一；否则序列号赋值为0，从0开始
        if (currentTimeMillis == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE_NUM;
            if (sequence == 0) {
                sequence = secureRandom.nextInt(Long.valueOf(SEQUENCE_BIT).intValue());
                currentTimeMillis = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = secureRandom.nextInt(Long.valueOf(SEQUENCE_BIT).intValue());
        }
        lastTimestamp = currentTimeMillis;
        long nextId = ((currentTimeMillis - EPOCH_STAMP) << TIMESTAMP_LEFT)
                | (dataCenterId << DATA_CENTER_LEFT)
                | (machineId << MACHINE_LEFT)
                | sequence;

        return nextId;
    }

    //获取时间戳，并与上次时间戳比较
    private static long tilNextMillis(long lastTimestamp) {
        long currentTimeMillis = currentTimeMillis();
        while (currentTimeMillis <= lastTimestamp) {
            currentTimeMillis = currentTimeMillis();
        }
        return currentTimeMillis;
    }

    //获取系统时间戳
    private static long currentTimeMillis() {
        return System.currentTimeMillis();
    }

    public static synchronized long nextValue() throws Exception {
        return getNextValue(MACHINE_ID, DATACENTER_ID);
    }

    public static synchronized long nextValue(long machineId) throws Exception {
        return getNextValue(machineId, DATACENTER_ID);
    }

    public static synchronized long nextValue(long machineId, long dataCenterId) throws Exception {
        return getNextValue(machineId, dataCenterId);
    }
}
